#include <stdio.h>
#include "lm_sys.h"
#include "lm_win.h"
#include "lm_misc.h"
#include "stdlib.h"
#include "lm_color.h"
#include "../common/lm_log.h"
#include "lm_dialog.h"
#include "../common/lm_list.h"
#include "../common/lm_mem.h"
#include "lm_listview.h"
#include "../parser/lm_parser.h"
#include "../parser/lm_macro.h"
#include "../parser/lm_string.h"
#include <getopt.h>


#define TERM_MIN_HEIGHT  16

static int select_color;

static lm_macro_t *menu = NULL;
static lm_macro_t *curr_macro = NULL;
static int curr_macro_pos = 0;
static char *config_header = "./config.h";
static bool defconf_flag = false;
static char *config_file = "./config.lm";
static char *make_file = "./make.mk";
static char *pro_name = "Project Menu Config";
static char *log_file = NULL;
static bool save_flag = false;

#define MACRO_SELECT_FLAG        "[y]"
#define MACRO_UNSELECT_FLAG      "[n]"


lm_color_t main_win_color = {
    .rgb_bg_index= 394,
    .rgb_fg_index = 315,
};

lm_color_t main_body_color = {
    .rgb_bg_index= 600,
    .rgb_fg_index = 256,
};


lm_color_t scroll_color = {
    .rgb_bg_index = 402,
    .rgb_fg_index = 427,
};


lm_color_t save_win_color = {
    .rgb_bg_index = 402,
    .rgb_fg_index = 427,
};

lm_color_t choice_win_color = {
    .rgb_bg_index = 402,
    .rgb_fg_index = 427,
};

lm_color_t depend_win_color = {
    .rgb_bg_index = 402,
    .rgb_fg_index = 427,
};


#define this_descp "This tool is used to configure macro dependency issues in your project. \
You can select macros using the Y and N buttons, and enter or exit using the left and right buttons"


#define author " [lsw: 1477153217@qq.com]"
#define version  "10.202400720"


typedef enum panel_button {
    BUTTON_SELECT = 0,
    BUTTON_BACK,
    BUTTON_EXIT,
    BUTTON_HELP,
    BUTTON_SAVE,

}panel_button_e;


void current_macro_init(void)
{
    curr_macro_pos = 0;
    menu = lm_parser_get_menu_root();
    curr_macro = lm_macro_get_menu_first_macro(menu);
}


void main_panel_button_show(lm_win_t *main_body, panel_button_e statue)
{
    const char button_opt[5][10]={"<Select>\0", "< Back >\0", "< Exit >\0", "< Help >\0", "< Save >\0"};
    int length = strlen(button_opt[0])+strlen(button_opt[1])+strlen(button_opt[2])  \
                +strlen(button_opt[3])+strlen(button_opt[4]);

    int space = (lm_win_get_width(main_body) - length)/10;
    int start_pos=(lm_win_get_width(main_body) - length - space*4)/2 ;

    for(int i = 0; i < 5; i ++){
        if(i == statue) {
            lm_win_set_pos_text_no_refresh(main_body, start_pos, lm_win_get_height(main_body) - 3, 
                                           button_opt[i], lm_color_swap(main_body->color));
        }
        else {
            lm_win_set_pos_text_no_refresh(main_body, start_pos, lm_win_get_height(main_body) - 3, 
                                           button_opt[i], lm_win_get_color(main_body));
        }
        start_pos += (strlen(button_opt[i]) + space);

        lm_win_refresh(main_body);
    }
}


panel_button_e main_panel_button_handler(lm_win_t *main_body, int kb_input)
{
    static int btn_status = BUTTON_SELECT;

    if(kb_input != KEYBOARD_INVALID) {
        if(kb_input == KEYBOARD_LEFT) {
            if(btn_status > (int)BUTTON_SELECT) {
                btn_status --;
            }
        }
        else if(kb_input == KEYBOARD_RIGHT) {
            if(btn_status < (int)BUTTON_SAVE) {
                btn_status ++;
            }
        }

        main_panel_button_show(main_body, btn_status);
        curs_set(0);
    }

    return (panel_button_e)btn_status;
}


bool main_active_save_btn_handler(lm_win_t *main_active)
{
    int h = lm_win_get_height(main_active) - 2, w = lm_win_get_width(main_active) - 8;

    char title[256] = "Are you sure to exit and save ";
    strcpy(title + strlen(title), make_file);

    const char* conf_mk = lm_edit_dialog_create((COLS - w)/2, (LINES - h)/2 + 2, w, h,
                                             title, make_file, save_win_color);
    if(conf_mk) {

        save_flag = true;

        //get menuconfig root
        lm_macro_t *root = lm_parser_get_menu_root();

        //save header file, default: config.h
        lm_parser_gen_header_file(root, config_header);
        //save config.mk file
        lm_parser_gen_config_file(root, conf_mk);
        //write all source file or object file list
        lm_parser_args_write_file(root, config_file, conf_mk);

        //save log file
        if(log_file) {
            FILE* file = fopen(log_file, "w");
            if (file == NULL) {
                printf("Failed to open or create the %s\n", log_file);
                return false;
            }
            //print all macro into log file
            lm_macro_print_all_attr(root, file);
            //close file
            fclose(file);
        }

        return true;
    }
    return false;
}


void main_active_help_btn_handler(lm_win_t *main_active)
{
    int h = lm_win_get_height(main_active) - 2, w = lm_win_get_width(main_active) - 8;
    lm_title_win_t *win;
    int kb_input;
    if(curr_macro) {
        win = lm_title_win_create(NULL, (COLS - w)/2, (LINES - h)/2 + 2, w, h, "macro dependent on", 
                            save_win_color);
        if(win != NULL) {
            if(curr_macro->depend) {
                lm_title_win_set_text(win, curr_macro->depend);
            }
            else {
                lm_title_win_set_text(win, "No dependencies!");
            }

            while(1) {
                kb_input = lm_get_kb_value();
                if(kb_input == KEYBOARD_ENTER) {
                    break;
                }
            }

            lm_title_win_delete(win);
        }
    }
}


void main_active_depend_warning(lm_win_t *main_active)
{
    int h = lm_win_get_height(main_active) - 2, w = lm_win_get_width(main_active) - 8;
    lm_title_win_t *win;
    int kb_input;
    if(curr_macro) {
        win = lm_title_win_create(NULL, (COLS - w)/2, (LINES - h)/2 + 2, w, h, "Unable to select, dependent on", 
                            depend_win_color);
        if(win != NULL) {
            if(curr_macro->depend) {
                lm_title_win_set_text(win, curr_macro->depend);
            }

            while(1) {
                kb_input = lm_get_kb_value();
                if(kb_input == KEYBOARD_ENTER) {
                    break;
                }
            }

            lm_title_win_delete(win);
        }
    }
}


static void __set_macro_text(lm_win_t *main_active, int x, int y, lm_macro_t *macro, int color)
{
    if(macro == NULL) {
        return;
    }

    char *value = macro->value;

    if(!macro->menu) {
        if(strcmp(value, "y") == 0) {
            lm_win_set_pos_text_no_refresh(main_active, x, y, macro->name, color);
            lm_win_set_pos_text_no_refresh(main_active, x + strlen(macro->name), y, " = ", color);
            lm_win_set_pos_text_no_refresh(main_active, x + strlen(macro->name) + 3, y, MACRO_SELECT_FLAG, color);
        }
        else if(strcmp(value, "n") == 0) {
            lm_win_set_pos_text_no_refresh(main_active, x, y, macro->name, color);
            lm_win_set_pos_text_no_refresh(main_active, x + strlen(macro->name), y, " = ", color);
            lm_win_set_pos_text_no_refresh(main_active, x + strlen(macro->name) + 3, y, MACRO_UNSELECT_FLAG, color);
        }
        else {
            lm_win_set_pos_text_no_refresh(main_active, x, y, macro->name, color);
            lm_win_set_pos_text_no_refresh(main_active, x + strlen(macro->name), y, " = ", color);
            lm_win_set_pos_text_no_refresh(main_active, x + strlen(macro->name) + 3, y, "[", color);
            lm_win_set_pos_text_no_refresh(main_active, x + strlen(macro->name) + 4, y, value, color);
            lm_win_set_pos_text_no_refresh(main_active, x + strlen(macro->name) + 4 + strlen(value), y, "]", color);
            
            //clear identify potential residual parts
            int start = x + strlen(macro->name) + 4 + strlen(value) + 1;
            int len = lm_win_get_width(main_active) - start - 4;
            for(int i = 0; i < len; i ++) {
                lm_win_set_pos_text_no_refresh(main_active, start + i, y, " ", lm_win_get_color(main_active));
            }
        }
    }
    else {
        lm_win_set_pos_text_no_refresh(main_active, x, y, macro->name, color);
        lm_win_set_pos_text_no_refresh(main_active, x + strlen(macro->name), y, " --->", color);
    }
}

void main_active_scroll_update(lm_win_t *scroll_bar)
{
    if(menu->count  > 1) {
        lm_vroll_set_value(scroll_bar, (double)curr_macro_pos / (double)(menu->count - 1));
    }
    else {
        lm_vroll_set_value(scroll_bar, (double)curr_macro_pos / (double)(menu->count));
    }
}


void main_active_menu_text_show(lm_win_t *main_active, int line)
{
    lm_macro_t *macro = lm_macro_get_menu_first_macro(menu);
    if(macro == NULL) {
        return;
    }

    int x_pos = lm_win_get_width(main_active) * 2/5, y_pos = 0;
    int height = lm_win_get_height(main_active) - 3;
    int offset = 0;

    lm_win_clear_text(main_active);

    if(curr_macro_pos > line) {
        offset = curr_macro_pos - line;
    }

    for(int i = 0; i < menu->count; i++) {
        if(y_pos > height) {
            break;
        }

        if(i >= offset) {
            if(y_pos == line) {
                __set_macro_text(main_active, x_pos, y_pos, macro, select_color);
            }
            else {
                __set_macro_text(main_active, x_pos, y_pos, macro, lm_win_get_color(main_active));
            }

            y_pos ++;
        }

        macro = lm_macro_list_get_next(macro);
    }

    lm_win_refresh(main_active);
}


//flag: true->enter  false: exit  
void main_active_switch_menu(lm_win_t *main_active, lm_win_t *scroll_bar, lm_win_t *main_descp_win, int *y_pos, bool flag)
{
    if(flag) {
        if(curr_macro->menu) {
            *y_pos = 0;
            curr_macro_pos = 0;
            menu = curr_macro;
            curr_macro = lm_macro_get_menu_first_macro(menu);
            main_active_menu_text_show(main_active, 0);
        }
    }
    else {
        curr_macro_pos = 0;
        *y_pos = 0;
        menu = curr_macro->parent;
        curr_macro = lm_macro_get_menu_first_macro(menu);
        main_active_menu_text_show(main_active, 0);
    }

    if(curr_macro->description) {
        lm_win_clear_text(main_descp_win);
        lm_win_set_text(main_descp_win, curr_macro->description);
    }
}


static char *macro_choice_select(lm_win_t *win, lm_macro_t *macro)
{
    int h = lm_win_get_height(win) - 2, w = lm_win_get_width(win) - 8;
    if(macro->menu) {
        return NULL;
    }

    LM_LIST_HEAD(listview);

    lm_array_t *array = &macro->choice;
    lm_list_node_t *node = lm_list_next_node(&array->head);
    lm_array_node_t *array_node = NULL;

    for(int i = 0; i < array->count; i++) {
        
        array_node = container_of(node, lm_array_node_t, node);

        if(array_node != LM_ERR_PTR) {
            if(array_node->string != NULL)
                lm_listview_add_item(&listview, array_node->string);
        }

        node = lm_list_next_node(node);
    }

    return lm_listview_create((COLS - w)/2, (LINES - h)/2 + 2, w, h, macro->name, &listview, choice_win_color);
}


int main_active_handler(lm_win_t *main_body, lm_win_t *main_active, lm_win_t *scroll_bar, lm_win_t *main_descp_win)
{
    int kb_input;
    int x_pos = lm_win_get_width(main_active) * 2/5, y_pos = 0;

    while(1) {
        kb_input = lm_get_kb_value();

        panel_button_e status = main_panel_button_handler(main_body, kb_input);

        if(kb_input == KEYBOARD_ENTER) {
            if(status == BUTTON_EXIT) {
                if(!save_flag) {
                    main_active_save_btn_handler(main_active);
                    return LM_OK;
                }
                else {
                    return LM_OK;
                }
            }
            else if(status == BUTTON_SAVE) {
                main_active_save_btn_handler(main_active);
                main_active_menu_text_show(main_active, y_pos);
            }
            else if(status == BUTTON_SELECT) {
                if(curr_macro->menu) {
                    main_active_switch_menu(main_active, scroll_bar, main_descp_win, &y_pos, true);
                }
                else if(curr_macro->value != NULL && 
                    strcmp(curr_macro->value, "y") != 0 && strcmp(curr_macro->value, "n") != 0) {
                    
                    if(lm_parser_get_macro_depend_value(curr_macro, true) == 0) {
                        main_active_depend_warning(main_active);
                    }
                    else {
                        char *choice = macro_choice_select(main_active, curr_macro);
                        if(choice != NULL) {
                            lm_macro_value_add(curr_macro, choice);
                            __set_macro_text(main_active, x_pos, y_pos, curr_macro, select_color);
                            lm_win_refresh(main_active);
                        }
                    }
                }
                else if(curr_macro->value != NULL && (strcmp(curr_macro->value, "y") == 0)) {
                    lm_macro_value_add(curr_macro, "n");
                }
                else if(curr_macro->value != NULL && (strcmp(curr_macro->value, "n") == 0)) {

                    if(lm_parser_get_macro_depend_value(curr_macro, true)) {
                        lm_macro_value_add(curr_macro, "y");
                    }
                    else {
                        main_active_depend_warning(main_active);
                    }
                }
                lm_parser_rescan_menu_macro(lm_parser_get_menu_root());

                __set_macro_text(main_active, x_pos, y_pos, curr_macro, select_color);
                main_active_menu_text_show(main_active, y_pos);
            }
            else if(status == BUTTON_BACK) {
                main_active_switch_menu(main_active, scroll_bar, main_descp_win, &y_pos, false);
            }
            else if(status == BUTTON_HELP) {
                main_active_help_btn_handler(main_active);
            }
        }
        else if(kb_input == KEYBOARD_UP) {

            if(curr_macro != lm_macro_get_menu_first_macro(menu)) {
                __set_macro_text(main_active, x_pos, y_pos, curr_macro, lm_win_get_color(main_active));

                if(y_pos == 0) {
                    lm_win_scrol_down(main_active);
                }
                else {
                    y_pos --;
                }

                curr_macro_pos --;
                curr_macro = lm_macro_list_get_prev(curr_macro);

                __set_macro_text(main_active, x_pos, y_pos, curr_macro, select_color);

                lm_win_refresh(main_active);
            }

            lm_vroll_set_value(scroll_bar, (double)curr_macro_pos / (double)(menu->count - 1));

            if(curr_macro->description) {
                lm_win_clear_text(main_descp_win);
                lm_win_set_text(main_descp_win, curr_macro->description);
            }
        }
        else if(kb_input == KEYBOARD_DOWN) {
            if(curr_macro != lm_macro_get_menu_last_macro(menu)) {
                __set_macro_text(main_active, x_pos, y_pos, curr_macro, lm_win_get_color(main_active));

                if(y_pos == (lm_win_get_height(main_active) - 3)) {
                    lm_win_scrol_up(main_active);
                }
                else {
                    y_pos ++;
                }

                curr_macro_pos ++;
                curr_macro = lm_macro_list_get_next(curr_macro);

                __set_macro_text(main_active, x_pos, y_pos, curr_macro, select_color);

                lm_win_refresh(main_active);
            }

            lm_vroll_set_value(scroll_bar, (double)curr_macro_pos / (double)(menu->count - 1));

            if(curr_macro->description) {
                lm_win_clear_text(main_descp_win);
                lm_win_set_text(main_descp_win, curr_macro->description);
            }
        }
        else if(kb_input == KEYBOARD_KEY_y || kb_input == KEYBOARD_KEY_Y) {
            if(status == BUTTON_SELECT) {
                if(curr_macro->value != NULL && 
                    (strcmp(curr_macro->value, "y") == 0 || strcmp(curr_macro->value, "n") == 0)) {

                    if(lm_parser_get_macro_depend_value(curr_macro, true)) {
                        lm_macro_value_add(curr_macro, "y");
                    }
                    else {
                        main_active_depend_warning(main_active);
                    }
                }
                lm_parser_rescan_menu_macro(lm_parser_get_menu_root());

                __set_macro_text(main_active, x_pos, y_pos, curr_macro, select_color);
                lm_win_refresh(main_active);
            }
        }
        else if(kb_input == KEYBOARD_KEY_n || kb_input == KEYBOARD_KEY_N) {
            if(status == BUTTON_SELECT) {

                if(curr_macro->value != NULL && 
                    (strcmp(curr_macro->value, "y") == 0 || strcmp(curr_macro->value, "n") == 0)) {

                    if(lm_parser_get_macro_depend_value(curr_macro, true)) {
                        lm_macro_value_add(curr_macro, "n");
                    }
                    else {
                        main_active_depend_warning(main_active);
                    }
                }
                lm_parser_rescan_menu_macro(lm_parser_get_menu_root());

                __set_macro_text(main_active, x_pos, y_pos, curr_macro, select_color);
                main_active_menu_text_show(main_active, y_pos);
            }
        }
        else if(kb_input == KEYBOARD_ESC) {
            main_active_switch_menu(main_active, scroll_bar, main_descp_win, &y_pos, false);
        }
        else if(kb_input == KEYBOARD_KEY_H || kb_input == KEYBOARD_KEY_h) {
            main_active_help_btn_handler(main_active);
            main_active_menu_text_show(main_active, y_pos);
        }

        main_active_scroll_update(scroll_bar);
    }
}


int main(int argc, char *argv[])
{
    int opt;

    while ((opt = getopt(argc, argv, "hvdf:c:o:n:l:")) != -1) {
        switch (opt) {
            case 'h':
                printf("Usage: %s [options]\n", argv[0]);
                printf("[options:]\n");
                printf("  -h, --help          Display this help message\n");
                printf("  -v, --version       Display the version of the lite-manager\n");
                printf("  -d, --defconfig     Execute make xxx_defconfig cmd, to generate config.h\n");
                printf("  -f, --lmfile        Project manager file[*.lm], default: %s\n", config_file);
                printf("  -c, --config        Project config file, default: %s\n", make_file);
                printf("  -o, --output        Header file output path, default: %s\n", config_header);
                printf("  -n, --name          Project name, default: no name\n");
                printf("  -l, --log           Log file default: no log\n");
                exit(0);
                break;
            case 'v':
                printf("lite-manager version %s\n", version);
                exit(0);
                break;
            case 'd':
                defconf_flag = true;
                break;
            case 'f':
                config_file = optarg;
                break;
            case 'c':
                make_file = optarg;
                break;
            case 'o':
                config_header = optarg;
                break;
            case 'n':
                pro_name = optarg;
                break;
            case 'l':
                log_file = optarg;
                break;
            case '?':
                printf("Unknown option: %c\n", optopt);
                exit(1);
                break;
        }
    }

    lm_sys_console_init();
    if(lm_win_get_term_height() < TERM_MIN_HEIGHT) {
        LM_LOG_ERROR("The terminal window size is too small");
        lm_sys_exit(1);
    }

    if(lm_mem_init()) {
        LM_LOG_ERROR("system memory pool alloc failed");
        lm_sys_exit(1);
    }

    int parser = LM_ERR;
    if(access(make_file, F_OK) != 0) {
        parser = lm_parser_main(NULL, config_file);
    }
    else {
        parser = lm_parser_main(make_file, config_file);
    }
    
    if(parser != LM_OK) {
        lm_sys_exit(1);
    }

    if(defconf_flag) {
        //get menuconfig root
        lm_macro_t *root = lm_parser_get_menu_root();
        //save header file, default: config.h
        lm_parser_gen_header_file(root, config_header);
        //save config.mk file
        lm_parser_gen_config_file(root, make_file);
        //write all source file or object file list
        lm_parser_args_write_file(root, config_file, make_file);

        lm_sys_exit(0);
    }

    lm_win_t *main_win = lm_win_create(NULL, 0, 0, 
                                       lm_win_get_term_width(), lm_win_get_term_height(), 
                                       main_win_color);

    if(main_win == NULL) {
        exit(1);
    }

    lm_win_t *main_body = lm_edge_win_create(main_win, 0, 1, 
                                             lm_win_get_width(main_win), 
                                             lm_win_get_height(main_win) - 1,
                                             main_win_color);
    if(main_body == NULL) {
        exit(1);
    }

    lm_win_t *main_active = lm_edge_win_create(main_body, 3, 4, 
                                               lm_win_get_width(main_body) - 8, 
                                               lm_win_get_height(main_body) - 8,
                                               main_body_color);
    if(main_active == NULL) {
        exit(1);
    }

    lm_win_t *scroll_bar = lm_create_vroll_bar(main_body, 
                                               lm_win_get_width(main_body) - 4, 4, 
                                               lm_win_get_height(main_body) - 8,
                                               scroll_color, 0.5);
    if(scroll_bar == NULL) {
        exit(1);
    }

    lm_win_t *main_descp_win =  lm_win_create(main_body, 0, 0, 
                                              lm_win_get_width(main_body) - 2, 3, 
                                              main_win_color);
    if(main_descp_win == NULL) {
        exit(1);
    }

    lm_win_set_text(main_descp_win, this_descp " @" author);
    

    lm_win_set_pos_text(main_win, (lm_win_get_term_width() - strlen(pro_name)) / 2, 0, 
                                    pro_name, lm_init_pair(705, main_win_color.rgb_bg_index));


    main_panel_button_show(main_body, BUTTON_SELECT);

    select_color = lm_init_pair(693, 650);

    current_macro_init();

    main_active_menu_text_show(main_active, 0);

    main_active_handler(main_body, main_active, scroll_bar, main_descp_win);

    lm_win_delete(main_descp_win);
    lm_win_delete(main_active);
    lm_win_delete(scroll_bar);
    lm_win_delete(main_body);
    lm_win_delete(main_win);

    lm_mem_destroy();
    lm_sys_exit(0);

    return 0;
}
